package com.ansion.log;

import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.ansion.time.TimeUtils;
import com.ansion.utils.DevicesUtils;
import com.tencent.mmkv.BuildConfig;


public class LOG {

    public static boolean SHOW_IN_LOGCAT = BuildConfig.DEBUG;

    public static final int FILE_LOG_MODE_FILE = 1;
    public static final int FILE_LOG_MODE_XLOG = 2;

    public static final int CONSOLE_LOG_MODE_LOGCAT = 1;
    public static final int CONSOLE_LOG_MODE_JVM = 2;


    public static String LOG_TAG = VLog.TAG;


    public static final int INFO = VLog.INFO;
    public static final int DEBUG = VLog.DEBUG;
    public static final int VERBOSE = VLog.VERBOSE;
    public static final int WARN = VLog.WARN;
    public static final int ERROR = VLog.ERROR;
    public static final int ALL = -1;


    private static Logger fileLogger;

    private static Logger consoleLogger;

    //1是用file logger，2是用xlog
    public static int FILE_LOG_MODE = FILE_LOG_MODE_XLOG;

    //控制台日志类型，1是logcat，2是JVM
    public static int CONSOLE_LOG_MODE = CONSOLE_LOG_MODE_LOGCAT;


    public static void v(@Nullable String msg) {
        internalLog(1, VERBOSE, LOG_TAG, null, msg);
    }

    public static void v(Throwable t, @Nullable String msg, Object... args) {
        internalLog(1, VERBOSE, LOG_TAG, t, msg, args);
    }

    public static void v(Throwable t) {
        internalLog(1, VERBOSE, LOG_TAG, t, null);
    }


    public static void d(Throwable t, @Nullable String msg, Object... args) {
        internalLog(1, DEBUG, LOG_TAG, t, msg, args);
    }

    public static void d(Throwable t) {
        internalLog(1, DEBUG, LOG_TAG, t, null);
    }

    public static void i(@Nullable String msg) {
        internalLog(1, INFO, LOG_TAG, null, msg);
    }

    public static void i(Throwable t, @Nullable String msg, Object... args) {
        internalLog(1, INFO, LOG_TAG, t, msg, args);
    }

    public static void i(Throwable t) {
        internalLog(1, INFO, LOG_TAG, t, null);
    }


    public static void w(Throwable t, @Nullable String msg, Object... args) {
        internalLog(1, WARN, LOG_TAG, t, msg, args);
    }

    public static void w(Throwable t) {
        internalLog(1, WARN, LOG_TAG, t, null);
    }

    public static void e(@Nullable String msg, Object... args) {
        internalLog(1, ERROR, LOG_TAG, null, msg, args);
    }

    public static void e(Throwable t, @Nullable String msg, Object... args) {
        internalLog(1, ERROR, LOG_TAG, t, msg, args);
    }

    public static void e(Throwable t) {
        internalLog(1, ERROR, LOG_TAG, t, null);
    }

    public static void log(int level, @NonNull String tag, @Nullable Throwable t,
                           @Nullable String msg, Object... args) {
        internalLog(1, level, tag, t, msg, args);
    }

    public static void d(@Nullable String tag, @Nullable String msg, Object... args) {
        d(tag, msg, null, args);
    }

    public static void v(@Nullable String tag, @Nullable String msg, Object... args) {
        v(tag, msg, null, args);
    }

    public static void i(@Nullable String tag, @Nullable String msg, Object... args) {
        i(tag, msg, null, args);
    }

    public static void w(@Nullable String tag, @Nullable String msg, Object... args) {
        w(tag, msg, null, args);
    }

    public static void e(@Nullable String tag, @Nullable String msg, Object... args) {
        e(tag, msg, null, args);
    }

    public static void v(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr, Object... args) {
        String finalTag = TextUtils.isEmpty(tag) ? LOG_TAG : String.format("%s[%s]", LOG_TAG, tag);
        internalLog(1, VERBOSE, finalTag, tr, msg, args);
    }

    public static void d(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr, Object... args) {
        String finalTag = TextUtils.isEmpty(tag) ? LOG_TAG : String.format("%s[%s]", LOG_TAG, tag);
        internalLog(1, DEBUG, finalTag, tr, msg, args);
    }

    public static void w(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr, Object... args) {
        String finalTag = TextUtils.isEmpty(tag) ? LOG_TAG : String.format("%s[%s]", LOG_TAG, tag);
        internalLog(1, WARN, finalTag, tr, msg, args);
    }


    public static void i(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr, Object... args) {
        String finalTag = TextUtils.isEmpty(tag) ? LOG_TAG : String.format("%s[%s]", LOG_TAG, tag);
        internalLog(1, INFO, finalTag, tr, msg, args);
    }

    public static void e(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr, Object... args) {
        String finalTag = TextUtils.isEmpty(tag) ? LOG_TAG : String.format("%s[%s]", LOG_TAG, tag);
        internalLog(1, ERROR, finalTag, tr, msg, args);
    }


    private static void internalLog(int callerStackIndex, int level, @NonNull String tag, @Nullable Throwable tr,
                                    @Nullable String msg, Object... args) {
        if (VLog.OPEN_LOG) {
            if (consoleLogger != null) {
                consoleLogger.log(new Throwable(), callerStackIndex + 1, level, tag, tr, msg, args);
            }
        }

        if (fileLogger != null) {
            fileLogger.log(new Throwable(), callerStackIndex + 1, level, tag, tr, msg, args);
        }
    }


    public static void logcat(int logLevel, @Nullable String tag, @Nullable String msg) {
        logcat(logLevel, null, tag, msg);
    }

    public static void logcat(int logLevel, @Nullable String msg) {
        logcat(logLevel, null, null, msg);
    }

    public static void logcat(int level, Throwable e) {
        logcat(level, e, null, null);
    }


    public static void logcat(int logLevel, @Nullable Throwable tr, @Nullable String tag, @Nullable String msg, Object... args) {
        if (SHOW_IN_LOGCAT) {
            if (consoleLogger != null) {
                String finalTag = TextUtils.isEmpty(tag) ? LOG_TAG : String.format("%s[%s]", LOG_TAG, tag);
                consoleLogger.log(new Throwable(), 2, logLevel, finalTag, tr, msg, args);
            }
        }
    }


    public static void setFileLogLevel(int level) {
        if (fileLogger != null) {
            fileLogger.setLogLevel(level);
        }
    }


    public static String getStackTraceString(Throwable tr) {
        return Log.getStackTraceString(tr);
    }

    public static void init(Logger injectFileLogger) {
        final String currentTime = TimeUtils.getCurrentTimeString();
        if (consoleLogger == null) {
            consoleLogger = (CONSOLE_LOG_MODE == CONSOLE_LOG_MODE_LOGCAT) ? new LogcatLogger() : new JvmLogger();
        }
        boolean initFileLog = fileLogger != null;
        fileLogger = injectFileLogger;

        if (initFileLog) {
            Log.e("LOG", " ");
            Log.e("LOG",
                    "#######################################################################################");
            Log.e("LOG",
                    "#  WARNING! WARNING! enabling file based log - this would degrade app performance!!!  #");
            Log.e("LOG",
                    "#######################################################################################");
            Log.e("LOG", " ");

        }
//        try {
//            if (fileLogger != null) {
//                fileLogger.log(null, 0, INFO, LOG_TAG, null, "+++ LOG start @ " + currentTime);
//                if (FILE_LOG_MODE == FILE_LOG_MODE_XLOG) {//如果是用xlog的话，则启动的时候flush一下
//                    fileLogger.flush();
//                }
//            }
//        } catch (Exception e) {
//
//            android.util.Log.e("LOG", "Unable to create file log @ " + e.getMessage());
//        }
    }


    static String crashHeader = "unknown";


    public static void saveCrashToLog(Throwable ex) {
        saveCrashToLog("", ex);
    }

    public static void saveCrashToLog(String reason, Throwable ex) {
        StringBuilder sb = new StringBuilder();
        if (!TextUtils.isEmpty(reason)) {

            sb.append(reason).append("\n");
        }
        sb.append("############# [T_CRASH]start ####################################").append("\n");
        sb.append(crashHeader).append("\n");
        LOG.e("----------------------------------------------------------"
                + DevicesUtils.getDeviceDescription());
        LOG.e("----------------------------------------------------------");
        if (ex != null) {
            sb.append(Log.getStackTraceString(ex));
            sb.append("!!!!!! Uncaught Exception from thread: ").append(ex.getClass().getName()).append("\n");
        }
        sb.append("############# [T_CRASH]end ######################################").append("\n");
        String log = sb.toString();
        LOG.e(log);
        S.e(log);
        flushLog();
    }


    public static void flushLog() {
        if (fileLogger != null)
            fileLogger.flush();
    }
}
